
#include "parser_state.hpp"
#include <cstdlib>

int yyerror(parser_state&,const void*) 
{ 
	return 0; 
}

void namespace_declare_function(var ns, var fn)
{
	var arity = fn["arguments"].size();
	var name = fn["name"];

	if( !ns["functions"].contains(name) )
		ns["functions"][name] = map();

	ns["functions"][name][arity] = fn;

	// !! Declare local vars...
}

var create_namespace()
{
	return object().extend
		("functions", map())
		("namespaces", map())
		("declare_function", namespace_declare_function);
}

var create_arg(var name, var default_value)
{
	return object().extend
		("name",name)
		("default",default_value);
}

var function_header(var ret, var name, var args)
{
	return object("function").extend
		("return_type",ret)
		("name",name)
		("arguments",args);
}

void parser_state::declare_function(var decl)
{
	current_namespace["declare_function"](decl);
	// Add to namespace
	current_function = decl;
}

void parser_state::define_function(var decl)
{
	current_namespace["declare_function"](decl);
	current_function = decl;
	current_function["stack_pos"] = 0;
	push_block();

	foreach( arg, decl["arguments"] )
	{
		if(arg["name"])
			declare_local_variable(arg["name"]);
	}
}

var exec_unvarargs(var fn, var args)
{
	switch(args.size())
	{
	case 0: return fn();
	case 1: return fn(args[0]);
	case 2: return fn(args[0],args[1]);
	case 3: return fn(args[0],args[1],args[2]);
	case 4: return fn(args[0],args[1],args[2],args[3]);
	case 5: return fn(args[0],args[1],args[2],args[3],args[4]);
	case 6: return fn(args[0],args[1],args[2],args[3],args[4],args[5]);
	case 7: return fn(args[0],args[1],args[2],args[3],args[4],args[5],args[6]);
	case 8: return fn(args[0],args[1],args[2],args[3],args[4],args[5],args[6],
				args[7]);
	case 9: return fn(args[0],args[1],args[2],args[3],args[4],args[5],args[6],
				args[7],args[8]);
	case 10: return fn(args[0],args[1],args[2],args[3],args[4],args[5],args[6],
				args[7],args[8],args[9]);
	default: throw exception("Too many arguments");
	}
}

var unvarargs(var fn)
{
	return bind(exec_unvarargs, fn);
}

void parser_state::declare_library_function(var name, var fn)
{
	var arity = fn.impl().max_args();

	if( !current_namespace["functions"].contains(name))
		current_namespace["functions"][name] = map();

	current_namespace["functions"][name][arity] = 
		object("builtin function").extend
			("return_type", true)
			("name",name)
			("eval",unvarargs(fn));
}

void parser_state::define_function_body(var block)
{
	// !! Check multiple definitions
	current_function["block"] = block;
	current_function["eval"] = block["execute"];
	pop_block();
}

void parser_state::push_namespace(var name)
{
	if(!current_namespace["namespaces"].contains(name))
	{;
		var ns = current_namespace["namespaces"][name] = create_namespace();
		ns["outer"] = current_namespace;
		current_namespace = ns;
	}
	else
	{
		current_namespace = current_namespace["namespaces"][name];
	}

}

void parser_state::pop_namespace()
{
	current_namespace = current_namespace["outer"];
}

void parser_state::parse_complete()
{
	var sm = current_namespace["functions"]["script_main"];
	if(sm == null) error_missing_script_main();
	sm = sm[1];
	if(sm == null) error_invalid_script_main();

	if( !sm["return_type"] ) error_invalid_script_main();

	if(!sm.contains("block")) error_missing_script_main();

	parse_result = sm["eval"];
}

void parser_state::error_syntax_error()
{
	error("syntax error", "Syntax error");
}

void parser_state::error_missing_script_main()
{
	error("missing script_main", "The function script_main() must be defined");
}

void parser_state::error_invalid_script_main()
{
	error("invalid script_main", "The function script_main() must take one argument and return var");
}

void parser_state::error_duplicate_variable(var name)
{
	error("duplicate", "The variable " + name + " has been defined twice in the same block");
}

void parser_state::error_not_evaluable()
{
	error("not_evaluable", "The expression cannot be evaluated");
}

void parser_state::error_unknown_identifier(var id)
{
	error("unknown_identifier", "The identifier '" + id + "' was not found");
}

void execute_block(var statements, var locals)
{
	foreach( s, statements ) s(locals);
}

void parser_state::push_block()
{
	current_block = object("block").extend
		("variables", map())
		("statements", array())
		("outer", current_block);

	current_block["execute"] = bind(execute_block, current_block["statements"]);
}

void parser_state::declare_local_variable(var name)
{
	writeln("Declaring local " + name + " to stack pos " + current_function["stack_pos"] );

	if( current_block["variables"].contains(name) )
		error_duplicate_variable(name);

	current_block["variables"][name] = current_function["stack_pos"]++;
}

void exec_decl_statement(var pos, var expr, var locals)
{
	var r = expr(locals);
	writeln("Setting local " + pos + " to " + r );
	locals[pos] = r;
}

void parser_state::declare_local_variable(var name, var expr)
{
	current_block["statements"].push_back(
		bind(exec_decl_statement, 
			current_function["stack_pos"], evaluate(expr)));
	declare_local_variable(name);
}

void parser_state::declare_default_local_variable(var name)
{
	declare_local_variable(name);
	// No need to assign null since variables have a unique stack position
}

var parser_state::pop_block()
{
	var prev_block = current_block;
	current_block = current_block["outer"];
	return prev_block;
}

var literal_eval(var value, var locals) 
{ 
	return value; 
}

var parser_state::literal_expression(var value)
{
	return object("literal").extend
		("eval", bind(literal_eval, value));
}

void exec_expr_statement(var expr, var locals)
{
	var r = expr(locals);
	writeln("Run expression = " + r);
}

void parser_state::expression_statement(var expr)
{
	current_block["statements"].push_back(bind(exec_expr_statement, evaluate(expr)));	
}

void exec_block(var statements, var locals)
{
	foreach( s, statements ) s(locals);
}

void parser_state::block_statement(var block)
{
	current_block["statements"].push_back(bind(exec_block,block["statements"]));
}

var parse_integer(const var & token)
{
	std::string toks = token.as_string();
	const char * str = toks.c_str();
	// str is in a valid integer format or the behaviour is undefined.

	int i=0;

	if(str[0] == '0')
	{
		if(tolower(str[1]) == 'x')
		{
			for(const char * p=str+2; *p; ++p)
			{
				int d = *p;
				if(isdigit(d)) d-='0';
				else if(isupper(d)) d-='A'-10;
				else d-='a'-10;
				i = 16*i + d;
			}
		}
		else
		{
			for(const char * p=str+1; *p; ++p)
			{
				i = 8*i + *p-'0';
			}
		}
	}
	else
	{
		for(const char * p=str; *p; ++p)
		{
			i = 10*i + *p-'0';
		}
	}
	return i;
}

bool ishexdigit(int ch)
{
	return isdigit(ch) || (ch>='a' && ch<='f') || (ch>='A' && ch<='F');
}

int tohex(int ch)
{
	if( isdigit(ch) ) return ch='0';
	if( ch>='a' && ch<='f') return ch-'a'+10;
	else return ch-'A'+10;
}

int read_ch(const char *& p)
{
	if(p[0] == '\\')
	{
		switch(p[1])
		{
		case '\"': p+=2; return '\"';
		case '\'': p+=2; return '\'';
		case 'r': p+=2; return '\r';
		case 'n': p+=2; return '\n';
		case 't': p+=2; return '\t';
		case '?': p+=2; return '\?';
		case '\\': p+=2; return '\\';
		case 'f': p+=2; return '\f';
		case 'a': p+=2; return '\a';
		case 'b': p+=2; return '\b';
		case 'v': p+=2; return '\v';
		case 'x': 
		case 'X':
			// Hexadecimal
			{
				p+=2;
				int i=0;
				for(int i=0; ishexdigit(*p); ++p)
				{
					i = i * 16 + tohex(*p);
				}
				return i;
			}
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
			// Octal
			{
				int i=*p++-'0';
				if(*p>='0' && *p<='7') i=i*8 + (*p++-'0');
				if(*p>='0' && *p<='7') i=i*8 + (*p++-'0');
				return i;
			}
		default:
			throw exception("impossible");
		}
	}
	else
	{
		return *p++;
	}
}

var parse_char(const var & token)
{
	std::string toks = token.as_string();
	const char * str = toks.c_str();

	if(str[0] == 'L')
		return wchar_t(read_ch(str+=2));
	else
		return char(read_ch(str+=1));
}

var parse_double(const var & token)
{
	return atof(token.as_string().c_str());
}

var parse_string_token(const var & token)
{
	std::string toks = token.as_string();
	const char * str = toks.c_str();

	// Not robust: input must come from a validated source
	var result;
	if(str[0] == 'L') ++str, result = L"";
	else result = "";
	++str;

	while(*str)
	{
		if(*str == '\"')
		{
			// Split strings...
			for(++str; *str && *str != '\"'; ++str)
				;
			if(*str) ++str;
		}
		else
			result.push_back(read_ch(str));
	}

	return result;
}

var eval_id(var stack_pos, var locals)
{
	return locals[stack_pos];
}

var eval_function(var fn, var locals)
{
	
}

var parser_state::lookup_variable(var block, var id)
{
	// !! Implement the assign method

	if( block["variables"].contains(id) )
		return object("id").extend
			("eval", bind(eval_id, block["variables"][id]));

	if(block["outer"]) return lookup_variable(block["outer"], id);

	// Look up function in current namespace

	
	var fn = current_namespace["functions"][id];

	if( fn.size() == 0 )
		error_unknown_identifier(id);

	var result = object("id");
	// !! Add function method

	if( fn.size() == 1 )
	{
		result.extend("eval", unvarargs(fn.front()));
	}

	return result;		
}

var parser_state::id_expression(var id)
{
	// !! Look up the ID here...

	if(id.size() > 1)
	{
		// !! This is a function
	}
	else
	{
		// This is a local variable or a function
		return lookup_variable(current_block, id[0]);		
	}

	writeln(id);
	return literal_expression("id_expression");
}

var add(var a, var b) { return a+b; } 

var eval_binary_expression(var fn, var lhs, var rhs, var locals)
{
	return fn(lhs(locals),rhs(locals));
}

var parser_state::evaluate(var expr)
{
	if(!expr.contains("eval")) error_not_evaluable();
	return expr["eval"];
}

var parser_state::binary_expression(var fn, var lhs, var rhs)
{
	// !! Validate the type of the lhs
	
	return object("binary").extend
		("eval", bind(eval_binary_expression, fn, evaluate(lhs), evaluate(rhs)));
}


var parser_state::throw_expression()
{
	return literal_expression(null);  // !! TODO
}

var parser_state::throw_expression(var x)
{
	return literal_expression(x);	// !! TODO
}